﻿using System;
using System.Linq;
using ContactCenter.Core.Common;
using ContactCenter.Persistence.NH.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NHibernate;
using NHibernate.Cfg;
using ContactCenter.Core.DomainModel;
using NHibernate.Criterion;
using SharpTestsEx;

namespace ContactCenter.EncryptionEventListener.Test
{
    /// <summary>
    /// Integration tests for Encryption Event Listener.
    /// </summary>
    [TestClass]
    public class EncryptionEventListenerFixture
    {
        const string ENCRYPTION_TEST_CUSTOMER_NAME = "EncryptionTestCustomer";
        const string CREDIT_CARD_PRIVATE_PART = "1234-5678-9101";

        // "Standard" configuration and Session Factory.
        private static ISessionFactory _sessionFactory;
        private static Configuration _configuration;

        // Configuration and Session Factory with encryption.
        private static ISessionFactory _encryptionSessionFactory;
        private static Configuration _encryptionConfiguration;

        [ClassInitialize]
        public static void Init(TestContext testContext)
        {
            _configuration = TaskExecutor.Execute(new SqliteDbTask(), new DomainMappingTask());
            _encryptionConfiguration = TaskExecutor.Execute(new SqliteDbTask(), new DomainMappingTask());
            //_encryptionConfiguration = TaskExecutor.Execute(new MsSqlDbTask().Force(), new DomainMappingTask());
        }

        [TestInitialize]
        public void Setup()
        {
            _sessionFactory = TaskExecutor.Execute(
                () => _configuration,
                new SchemaExportTask()
            ).BuildSessionFactory();

            _encryptionSessionFactory = TaskExecutor.Execute(
                () => _encryptionConfiguration,
                new EncryptionListenerTask()
            ).BuildSessionFactory();
        }

        private static ISession GetSessionWithoutEncryption()
        {
            return _sessionFactory.OpenSession();
        }

        private static ISession GetSessionWithEncryption()
        {
            return _encryptionSessionFactory.OpenSession();
        }

        private static Customer CreateTestCustomer()
        {
            var cust = Customer.Create(ENCRYPTION_TEST_CUSTOMER_NAME);
            cust.ChangeCreditCard(new CreditCard("9999", CREDIT_CARD_PRIVATE_PART, DateTime.Today.AddYears(1)));
            cust.ChangeContactInfo(new ContactInfo("123456789", "132456789", "123456789", "info@encryptiontestcustomer.it"));
            cust.AddAddress(new Address(cust, "address", "12345", "city", "region"));
            return cust;
        }

        private static void LoadTestData()
        {
            using (var session = GetSessionWithEncryption())
            {
                using (var tx = session.BeginTransaction())
                {
                    for (var i = 1; i < 4; i++)
                    {
                        var cust = Customer.Create("Customer" + i);
                        cust.ChangeCreditCard(new CreditCard("1234-5678-9101", "1121", DateTime.Today.AddYears(1)));
                        cust.ChangeContactInfo(new ContactInfo("123456789", "132456789", "123456789", string.Format("info@customer{0}.it", i)));
                        cust.AddAddress(new Address(cust, "address" + i, "12345", "city" + i, "region" + i));
                        session.Save(cust);
                    }
                    tx.Commit();
                }
            }
        }

        /// <summary>
        /// Gets or sets the test context which provides
        /// information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext { get; set; }

        [TestMethod]
        public void CreateData()
        {
            LoadTestData();
            Assert.IsTrue(1==1);
        }

        [TestMethod]
        public void CustomerCreditCardShouldBeEncrypted()
        {
            var cust = CreateTestCustomer();

            using (var session = GetSessionWithEncryption())
            {
                using (var tx = session.BeginTransaction())
                {
                    session.Save(cust);
                    tx.Commit();
                }
            }

            Customer encryptedCustomer = null;
            using (var session = GetSessionWithoutEncryption())
            {
                using (var tx = session.BeginTransaction())
                {
                    encryptedCustomer = session.CreateCriteria<Customer>()
                        .Add(Restrictions.Eq("CustomerName", ENCRYPTION_TEST_CUSTOMER_NAME))
                        .List<Customer>()
                        .FirstOrDefault();
                    tx.Commit();
                }
            }

            encryptedCustomer.CreditCard.PrivatePart.Should().Not.Be.EqualTo(CREDIT_CARD_PRIVATE_PART);
        }

        [TestMethod]
        public void ShouldEncryptAndDecrypt()
        {
            var cust = CreateTestCustomer();

            using (var session = GetSessionWithEncryption())
            {
                using (var tx = session.BeginTransaction())
                {
                    session.Save(cust);
                    tx.Commit();
                }
            }

            Customer encryptedCustomer = null;
            using (var session = GetSessionWithEncryption())
            {
                using (var tx = session.BeginTransaction())
                {
                    encryptedCustomer = session.CreateCriteria<Customer>()
                        .Add(Restrictions.Eq("CustomerName", ENCRYPTION_TEST_CUSTOMER_NAME))
                        .List<Customer>()
                        .FirstOrDefault();
                    tx.Commit();
                }
            }

            encryptedCustomer.CreditCard.PrivatePart.Should().Be.EqualTo(CREDIT_CARD_PRIVATE_PART);
        }
    }
}
